查看原文
其他

SuperEdge 如何支持多地域 StatefulSets 及灰度

陈凯悦 腾讯云原生 2021-07-14

陈凯悦,腾讯云容器技术研发工程师,腾讯云TKE后台研发,腾讯云边缘容器核心开发成员。

SuperEdge 介绍

SuperEdge 是 Kubernetes 原生的边缘容器方案,它将 Kubernetes 强大的容器管理能力扩展到边缘计算场景中,针对边缘计算场景中常见的技术挑战提供了解决方案,如:单集群节点跨地域、云边网络不可靠、边缘节点位于 NAT 网络等。这些能力可以让应用很容易地部署到边缘计算节点上,并且可靠地运行,可以帮助您很方便地把分布在各处的计算资源放到一个 Kubernetes 集群中管理,包括但不限于:边缘云计算资源、私有云资源、现场设备,打造属于您的边缘 PaaS 平台。SuperEdge[1]支持所有 Kubernetes 资源类型、API 接口、使用方式、运维工具,无额外的学习成本,也兼容其他云原生项目,如:Promethues,使用者可以结合其他所需的云原生项目一起使用。项目由以下公司共同发起:腾讯、Intel、VMware、虎牙直播、寒武纪、首都在线和美团。

SuperEdge 中的 ServiceGroup 功能可以便捷地在共属同一个集群的不同机房或区域中各自部署一组服务,只需要使用 ServiceGroup 提供的 DeploymentGrid 和 ServiceGrid 两种资源,即可方便地将服务分别部署到这些节点组中,保证各区域服务数量及容灾,并进行服务流量管控,使得各个服务间的请求在本机房或本地域内部即可完成,避免服务跨地域访问。

在最新发布的 SuperEdge release 0.2.0 版本中:SuperEdge 扩展了有状态应用类型,引入了对应 StatefulSets 的 StatefulSetGrid,方便有状态应用在多地域的独立部署和扩展;流量区域自治能力相应也支持了 StatefulSets 常配对的 Headless Service;同时新增了按照地域进行灰度的功能,也即各 NodeUnit 内独立部署的 workload 版本可以不同。

有状态应用的支持

支持 StatefulSets

最新版本的 SuperEdge 中,ServiceGroup 支持了有状态应用 StatefulSets。除了保持和原生 StatefulSets 字段的完全一致、方便已有应用改造、无需担心同步社区最新特性的同时,也继续保持了 ServiceGroup 的核心特性,能便捷地在一个集群中的多个地域进行边缘应用的独立部署和统一运维。

如下图所示,一个 CDN 集群需要在 zone-1 和 zone-2 两个地域的机房内各完整部署一套 StatefulSets 应用,但是两个地域网络不互通

此时可以在云端部署下发如下格式的 StatefulSetGrid

apiVersion: superedge.io/v1
kind: StatefulSetGrid
metadata:
spec:
  gridUniqKey: <NodeLabel Key>
  <statefulset-template>

即可方便地在两个机房内自动部署对应的 StatefulSets:StatefulSets-zone-1, StatefulSets-zone-2;同时保证云端与边缘端应用的强一致,修改云端资源即可同步到边端。

同时其中的拓扑 key,也即gridUniqKey可以自行定义,相较于社区目前实现的三种选择:"kubernetes.io/hostname","topology.kubernetes.io/zone"以及"topology.kubernetes.io/region",使用起来更加灵活。

另外,当该 CDN 集群纳管了新区域的机房时,无需在新的机房内手动部署 StatefulSets,ServiceGroup 会自动在该新增地域内部署一套独立的 StatefulSets,极大地方便了边缘有状态应用的部署和运维。

流量区域自治能力支持 Headless Service

由于地域之间的网络限制,不同地域的机房并不互通,ServiceGroup 提出了 ServiceGrid 的概念,无需在各个地域都部署一个对应本地 workload 的 Service。只需部署 ServiceGrid 资源,生成的一个 Service 就会对应所有地域的 workload,但在各地域内访问 Service 时能够保证流量的后端只限制在本地域内,保证了流量的区域自治能力。

对于 StatefulSets 而言,其每个 Pod 的名称都是有序且固定的,因为这个特性,经常和 Headless Service 搭配起来使用。Headless Service 对应的每一个 Endpoints 都会有一个固定的对应的 DNS 域名,Pod 之间可以相互访问,集群也可以单独访问指定的 Pod。

针对这个特点,StatefulSetGrid 支持使用 Headless Service 的方式进行访问,如下所示:

StatefulSetGrid 提供屏蔽 NodeUnit 的统一 Headless Service 访问形式:{StatefulSetGrid}-{0..N-1}.{ServiceGrid}-svc.ns.svc.cluster.local,上述访问会对应实际各个 NodeUnit 的具体 Pod:{StatefulSetGrid}-{NodeUnit}-{0..N-1}.{ServiceGrid}-svc.ns.svc.cluster.local

举个例子,部署如下的 StatefulsetGrid 和 ClusterIP 为 None 的 ServiceGrid

apiVersion: superedge.io/v1
kind: StatefulSetGrid
metadata:
  name: statefulsetgrid-demo
  namespace: default
spec:
  gridUniqKey: zone
  template:
    selector:
      matchLabels:
        appGrid: echo
    serviceName: "servicegrid-demo-svc"
    replicas: 3
    template:
      metadata:
        labels:
          appGrid: echo
      spec:
        terminationGracePeriodSeconds: 10
        containers:
        - image: superedge/echoserver:2.2
          name: echo
          ports:
          - containerPort: 8080
            protocol: TCP
          env:
            - name: NODE_NAME
              valueFrom:
                fieldRef:
                  fieldPath: spec.nodeName
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
            - name: POD_IP
              valueFrom:
                fieldRef:
                  fieldPath: status.podIP
          resources: {}
apiVersion: superedge.io/v1
kind: ServiceGrid
metadata:
  name: servicegrid-demo
  namespace: default
spec:
  gridUniqKey: zone
  template:
    selector:
      appGrid: echo
    ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
    clusterIP: None

如上资源会创建出一个名为servicegrid-demo-svc的 Service,同时在各地域创建名为statefulsetgrid-demo-的 StatefulSets。

每个 NodeUnit 内通过相同的 Headless Service 只会访问本组内的 Pod。也即,对于NodeUnit:zone-1来说,会访问statefulsetgrid-demo-zone-1(StatefulSets)对应的 Pod;而对于NodeUnit:zone-2来说,会访问statefulsetgrid-demo-zone-2(StatefulSets)对应的 Pod。

# execute on zone-1 nodeunit
[~]# curl statefulsetgrid-demo-0.servicegrid-demo-svc.default.svc.cluster.local|grep "pod name"
        pod name:       statefulsetgrid-demo-zone-1-0
[~]# curl statefulsetgrid-demo-1.servicegrid-demo-svc.default.svc.cluster.local|grep "pod name"
        pod name:       statefulsetgrid-demo-zone-1-1
[~]# curl statefulsetgrid-demo-2.servicegrid-demo-svc.default.svc.cluster.local|grep "pod name"
        pod name:       statefulsetgrid-demo-zone-1-2
...
# execute on zone-2 nodeunit
[~]# curl statefulsetgrid-demo-0.servicegrid-demo-svc.default.svc.cluster.local|grep "pod name"
        pod name:       statefulsetgrid-demo-zone-2-0
[~]# curl statefulsetgrid-demo-1.servicegrid-demo-svc.default.svc.cluster.local|grep "pod name"
        pod name:       statefulsetgrid-demo-zone-2-1
[~]# curl statefulsetgrid-demo-2.servicegrid-demo-svc.default.svc.cluster.local|grep "pod name"
        pod name:       statefulsetgrid-demo-zone-2-2

由于 Kube-Proxy 不会处理 Headless Service,所以之前通过 wrapper 代理 Kube-Proxy 的方式行不通。

这里设计新增了 statefulset-grid-daemon 组件,该组件根据 StatefulSets 构建和更新对应的{StatefulSetGrid}-{0..N-1}.{StatefulSetGrid}-svc.ns.svc.cluster.local DNS A record,结合 CoreDns 的 Host plugins,使得 statefulset-grid-daemon 可以添加原来 CoreDns 不存在的 domain record,并且生效。

灰度功能

基于系统稳定性和快速业务迭代的考虑,许多团队使用了服务灰度发布的方式,也就是并非一次性将服务发布到全部的地域,而是只发布指定的几个地域,经过验证没有问题后再全量发布;抑或是进行 A/B Test,可能同时存在 v1、v2、v3 等多个版本的实例,根据实际反馈和对比选择更为适合的版本进行全量发布。

在边缘场景中,还会存在一个集群纳管的不同地域的机房需要部署不同版本应用程序的情况。比如一条高速公路上不同省份段的摄像头,都需要独立部署应用,该应用具有 A,B,C 等多种功能,可以通过应用的启动参数进行控制。但由于道路跨多省,不同省份的摄像头需要的功能不同,有的只需要 A 功能,有的只需要 B 功能,也即有不同地域部署不同应用的需求。这种场景下,也需要用到灰度的功能。

最新版 SuperEdge 的 ServiceGroup 支持了灰度功能,同时由于 ServiceGroup 的地域属性,DeploymentGrid 和 StatefulSetGrid 均支持按照 NodeUnit 进行地域维度的灰度。

所有地域使用相同的版本

这种情况使用相同的 template 创建 workload,则无需添加任何额外字段

使用不同的 template 创建 workload

支持 template 中包含 image, replicas 等在内的任意字段的灰度

apiVersion: superedge.io/v1
kind: DeploymentGrid
metadata:
  name: deploymentgrid-demo
  namespace: default
spec:
  defaultTemplateName: test1
  gridUniqKey: zone
  template:
    replicas: 1
    selector:
      matchLabels:
        appGrid: echo
    strategy: {}
    template:
      metadata:
        creationTimestamp: null
        labels:
          appGrid: echo
      spec:
        containers:
        - image: superedge/echoserver:2.2
          name: echo
  templatePool:
    test1:
      replicas: 2
      selector:
        matchLabels:
          appGrid: echo
      strategy: {}
      template:
        metadata:
          creationTimestamp: null
          labels:
            appGrid: echo
        spec:
          containers:
          - image: superedge/echoserver:2.5
            name: echo
    test2:
      replicas: 3
      selector:
        matchLabels:
          appGrid: echo
      strategy: {}
      template:
        metadata:
          creationTimestamp: null
          labels:
            appGrid: echo
        spec:
          containers:
          - image: superedge/echoserver:2.3
            name: echo
            ports:
            - containerPort: 8080
              protocol: TCP
            env:
              - name: NODE_NAME
                valueFrom:
                  fieldRef:
                    fieldPath: spec.nodeName
  templates:
    zone1: test1
    zone2: test2

templatePool 代表待用的 template 实例列表,templates 代表 template 和 NodeUnit 的对应关系。这个例子中,NodeUnit zone1 地域将会使用名为 test1 的 template,NodeUnit zone2 地域将会使用名为 test2 的 template,其余 NodeUnit 地域将会使用 defaultTemplateName 中指定的 template,这里指定的是 test1 template,如果 defaultTemplateName 不指定或者指定为default,则使用spec.template中的模板。更多字段和功能说明可以查看文档介绍[2]

另外针对由 ServiceGroup 生成的各地域 workload 可能存在的负载不同情况,可以针对各地域 workload 分别设置不同的 HPA 策略,replicas 字段不会被强制更新。

总结

当前 ServiceGroup 已具备常用的边缘应用管理能力,也在多个实际业务场景中应用落地,获得业务广泛认可。我们仍然会继续演进,提供更多有意义的能力,也欢迎对边缘计算感兴趣的公司、组织及个人一起共建 SuperEdge 边缘容器项目。

(欢迎加入SuperEdge边缘容器微信沟通群)

参考资料

[1]

SuperEdge:( https://github.com/superedge/superedge)

[2]

文档介绍: (https://github.com/superedge/superedge/blob/main/docs/components/serviceGroup.md#canary-deployment)





  往期精选推荐  


    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存